﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Runtime.Serialization;

namespace WWWSiteHelper
{
    public class TagAttributeCountingKnowledge
    {
        public class TagTraitCount
        {
            public TagTraitCount(int opened, int closed)
            {
                this.Opened = opened;
                this.Closed = closed;
            }

            public int Opened { get; set; }
            public int Closed { get; set; }
        }
        private string lastTag = null;
        private IDictionary<string, TagTraitCount> tagsCount;
        private IDictionary<string, IDictionary<string, int>> attributesCount;

        private TagAttributeCountingKnowledge()
        {
            tagsCount = new Dictionary<string, TagTraitCount>();
            attributesCount = new Dictionary<string, IDictionary<string, int>>();
        }

        public TagAttributeCountingKnowledge(List<TagAttributeCountingKnowledge> knowledges)
        {
            tagsCount = knowledges
                .SelectMany(knowledge => knowledge.tagsCount)
                .ToDictionary(tagCount => tagCount.Key, tagCount => tagCount.Value);
            attributesCount = knowledges
                .SelectMany(knowledge => knowledge.attributesCount)
                .SelectMany(attribute => attribute.Value
                    .Select(exactAttr => TagAttributeToCount(attribute.Key, exactAttr.Key, exactAttr.Value)))
                .GroupBy(tagAttrToCount => tagAttrToCount.Key)
                .ToDictionary(entry => entry.Key, entry => entry.Sum(tagAttrToCount => tagAttrToCount.Value))
                .GroupBy(tagAttrToCount => tagAttrToCount.Key.Key)
                .ToDictionary(
                    tagToRest => tagToRest.Key,
                    tagToRest => (IDictionary<string, int>)tagToRest
                        .GroupBy(tagAttrToCount => tagAttrToCount.Key.Value)
                        .ToDictionary(
                            attrToRest => attrToRest.Key,
                            attrToRest => attrToRest.First().Value));
        }

        public static TagAttributeCountingKnowledge parse(string content)
        {
            TagAttributeCountingKnowledge result = new TagAttributeCountingKnowledge();
            HTMLParserState startState = HTMLParserState.Text;
            List<HTMLParserState> acceptingStates = new List<HTMLParserState>();
            acceptingStates.Add(HTMLParserState.Text);
            StackBehaviorProvider<HTMLParserState> behaviorProvider
                = new StackBehaviorProvider<HTMLParserState>(new SortedSet<HTMLParserState>(new HTMLParserState[]{
                    HTMLParserState.TagName,
                    HTMLParserState.AttributeName,
                    HTMLParserState.CloseTagName}));
            AutomatonWithStack<HTMLParserState> automaton
                = new AutomatonWithStack<HTMLParserState>(
                    startState,
                    acceptingStates,
                    HTMLStateTransition.Transition,
                    result.GiveBackFunction,
                    behaviorProvider.DefaultBehavior);
            automaton.Execute(content);
            //System.Console.WriteLine(String.Join(", ", result.getTags().Keys.ToList()));
            return result;
        }

        public IDictionary<string, int> getAttributes(string tag)
        {
            if (attributesCount.ContainsKey(tag))
            {
                return new SortedDictionary<string, int>(attributesCount[tag]);
            }
            return new SortedDictionary<string, int>();
        }

        public IDictionary<string, TagTraitCount> getTags()
        {
            return new SortedDictionary<string, TagTraitCount>(tagsCount);
        }

        private KeyValuePair<KeyValuePair<string, string>, int> TagAttributeToCount(string tag, string attibute, int count)
        {
            return new KeyValuePair<KeyValuePair<string, string>, int>(new KeyValuePair<string, string>(tag, attibute), count);
        }

        private void GiveBackFunction(HTMLParserState prevState, HTMLParserState state, String token)
        {
            if (prevState == HTMLParserState.CloseTagName)
            {
                if (!tagsCount.ContainsKey(token))
                {
                    tagsCount[token] = new TagTraitCount(0, 0);
                }
                ++tagsCount[token].Closed;
                lastTag = token;
            }
            else if (prevState == HTMLParserState.TagName)
            {
                if (!tagsCount.ContainsKey(token))
                {
                    tagsCount[token] = new TagTraitCount(0, 0);
                }
                ++tagsCount[token].Opened;
                lastTag = token;
            }
            else if (prevState == HTMLParserState.AttributeName)
            {
                if (!attributesCount.ContainsKey(lastTag))
                {
                    attributesCount[lastTag] = new SortedDictionary<string, int>();
                }
                if (!attributesCount[lastTag].ContainsKey(token))
                {
                    attributesCount[lastTag][token] = 0;
                }
                ++attributesCount[lastTag][token];
            }
        }
    }
}
